package com.example.mq.common;

import com.example.mq.mqserver.core.ExchangeType;

/**
 * 这个类对 bindingKey和routingKey 做进一步的处理
 */
public class Router {

    //  bindingKey 的构造格式
    //  数字，字母，下划线，* / # 两种通配符
    //  注意：# 可以代表空串，*不行
    //  使用 . 分割每个片段，通配符单独作为一个片段
    public static boolean checkBindingKey(String bindingKey) {
        if(bindingKey.length() == 0) {
            //  direct/fanout 的交换机bindingKey无意义，设为""
            return true;
        }
        //  1. 验证 key中是否有非法字符
        for(int i = 0;i < bindingKey.length();i++) {
            char ch = bindingKey.charAt(i);
            if(ch >= 'A' && ch <= 'Z') {
                continue;
            }
            if(ch >= 'a' && ch <= 'z') {
                continue;
            }
            if(ch >= '0' && ch <= '9') {
                continue;
            }
            if(ch == '.' || ch == '*'  || ch == '#' || ch == '_') {
                continue;
            }
            return false;
        }

        //  2. 检查通配符（*/#）是否单独作为一个片段
        // aaa.*.bbb 合法情况;  aaa.a*.bbb 非法情况.
        String[] words = bindingKey.split("\\.");//  正则表达式中 \. 才能输入.
        for (String word : words) {
            if (word.length() > 1 && (word.contains("*") || word.contains("#"))) {
                return false;
            }
        }

        //  3. 约定通配符的相邻关系（人为约定）
        //  aaa.#.#.bbb 不合法
        //  aaa.#.*.bbb 不合法
        //  aaa.*.#.bbb 不合法
        //  aaa.*.* bbb 合法
        //  这么约定是因为前三种实现匹配逻辑会更复杂，但性能提升不大
        for(int i = 0;i < bindingKey.length() - 1;i++) {
            char prev = bindingKey.charAt(i);
            char cur = bindingKey.charAt(i + 1);
            if(prev == '#' && cur == '#') {
                return false;
            }
            if(prev == '#' && cur == '*') {
                return false;
            }
            if(prev == '*' && cur == '#') {
                return false;
            }
        }

        return true;
    }

    //  routingKey 的构造格式
    //  数字，字母 ，下划线，无通配符
    //  使用 . 分割每个片段
    public static boolean checkRoutingKey(String routingKey) {
        if(routingKey.length() == 0) {
            //  fanout 交换机时，routingKey 无意义，设置为""
            return true;
        }
        //  只需要检查是否包含非法字符即可
        for(int i = 0;i < routingKey.length();i++) {
            char ch = routingKey.charAt(i);
            if(ch >= 'A' && ch <= 'Z') {
                continue;
            }
            if(ch >= 'a' && ch <= 'z') {
                continue;
            }
            if(ch >= '0' && ch <= '9') {
                continue;
            }
            if(ch == '.' || ch == '_') {
                continue;
            }
            return false;
        }

        return true;
    }

    //  判断当前交换机类型下 bindingKey 与 routingKey是否匹配
    public static boolean route(ExchangeType exchangeType,String bindingKey, String routingKey) throws MqException {
        //todo
        if(exchangeType == ExchangeType.FANOUT) {
            //  fanout 交换机是转发至所有队列，bindingKey无意义
            return true;
        }
        else if(exchangeType == ExchangeType.TOPIC) {
            return routeTopic(bindingKey,routingKey);
        }
        else {
            throw new MqException("交换机类型非法! ");
        }
    }

    private static boolean routeTopic(String bindingKey, String routingKey) {
        String[] bindingKeyTokens = bindingKey.split("\\.");
        String[] routingKeyTokens = routingKey.split("\\.");
        int bindIndex = 0;
        int routIndex = 0;
        while(bindIndex < bindingKeyTokens.length && routIndex < routingKeyTokens.length) {
            if(bindingKeyTokens[bindIndex].equals("*")) {
                //  遇到 '*'，两边下标同时后移即可
                bindIndex++;
                routIndex++;
            } else if(bindingKeyTokens[bindIndex].equals("#")) {
                //  遇到 '#'，让 bindingKeyTokens 的下一个片段去 routingKeyTokens 做匹配
                if(++bindIndex == bindingKeyTokens.length) {
                    return true;
                }
                //  重新计算匹配过后的routIndex
                routIndex = findNextMatch(bindingKeyTokens[bindIndex],routingKeyTokens,routIndex);
                if(routIndex == -1) {
                    return false;
                }
                bindIndex++;
                routIndex++;
            } else {
                //  普通字符串，没有通配符，内容要完全一样
                if(!bindingKeyTokens[bindIndex].equals(routingKeyTokens[routIndex])) {
                    return false;
                }
                bindIndex++;
                routIndex++;
            }
        }

        //  当越界时，要判断是否两边都已经全部匹配了
        if(bindIndex == bindingKeyTokens.length && routIndex == routingKeyTokens.length) {
            return true;
        }
        return false;
    }

    //  寻找下一个匹配的片段
    //  成功返回当前 routIndex  失败返回 -1
    private static int findNextMatch(String target,String[] routingKeyTokens,int routIndex) {
        //  # 可以代替空片段
        //  所以先从routIndex后一个开始找，没找到在判断当前位置是否相同
        for(int i = routIndex + 1;i < routingKeyTokens.length;i++) {
            if(target.equals(routingKeyTokens[i])) {
                return i;
            }
        }

        if(routingKeyTokens[routIndex].equals(target)) {
            //  后面没有匹配的片段，当前 # 只能作为空片段
            return routIndex;
        }
        return -1;
    }

}
